//
// Created by shirley on 2024/12/27.
//

#include "PickingSpots.h"

#include <unordered_set>

vector<int> PickingSpots::Picking(const size_t& size)
{
    if (type == "Average_picking")
        return Average_picking(size);
    else
        return {};
}

vector<int> PickingSpots::Picking(finiteGrid& grid, int index, string mode)
{
    if (type == "Average_picking")
        return Average_picking(grid, index, mode);
    else
        return {};
}

vector<int> PickingSpots::Picking(const vector<int>& indexs)
{
    if (type == "Average_picking")
        return Average_picking(indexs);
    else
        return {};
}

void PickingSpots::setCoarseFactor(double CoarseFactor)
{
    this->CoarseFactor = CoarseFactor;
}

void PickingSpots::setMin_Rate(double rate)
{
    this->Min_Rate = rate;
}


vector<int> PickingSpots::Average_picking(const size_t& size) const
{
    int num = size * CoarseFactor;
    double step = size/num;
    vector<int> result(num);
    for (int i = 0; i < num; i++)
    {
        result[i] = i * step;
    }
    return result;
}

vector<int> PickingSpots::Average_picking(finiteGrid& grid, int index, string mode) const
{
    int layers = 5;
    int num_sample = 0;
    vector<int> result;
    //采样点坐标
    vector<vector<int>> temp_result;
    //采样区域是否全采标志
    bool flag = false;

    /*
     * 根据采样规则获取样本坐标
     * mode = "row"时，取样方式为按ij平面采样
     * mode = "column"时，取样方式为按jk平面采样
     * mode = "hole"时，取样方式为整体采样
     * 采样方式均为: 若当前区域点数量 < 1000,则选取全部取样点，反之则按照由外到内逐层减少的策略进行取样
     */
    if (mode == "row")
    {
        flag = (grid.topo_i * grid.topo_j < 1000);
        temp_result = sampleGrid(grid.reservior_relative_i_start, grid.topo_i, grid.reservior_relative_j_start, grid.topo_j,layers,flag);
    }
    else if (mode == "column")
    {
        flag = (grid.topo_k * grid.topo_j < 1000);
        temp_result = sampleGrid(grid.reservior_relative_k_start, grid.topo_k, grid.reservior_relative_j_start, grid.topo_j,layers,flag);

    }
    else if (mode == "hole")
    {
        printf("hole mode is not currently supported!\n");
        flag = (grid.topo_k * grid.topo_j * grid.topo_i < 5000);
        if (flag)
            num_sample = grid.topo_k * grid.topo_j * grid.topo_i;
        else
        {
        }
    }
    else
        printf("mode error!\n");

    num_sample = temp_result.size();

    //获取采样点索引id
    result.resize(num_sample);
    int sum = 0;
    for (auto point: temp_result)
    {
        if (mode == "row")
            result[sum] = grid.get_hex_id(point[0], point[1], index, grid.i_withext, grid.j_withext, grid.k_withext);
        else if (mode == "column")
            result[sum] = grid.get_hex_id(index, point[1], point[0], grid.i_withext, grid.j_withext, grid.k_withext);
        sum++;
    }

    return result;
}

vector<int> PickingSpots::Average_picking(const vector<int>& indexs) const
{
    if (indexs.size() == 0)
    {
        cout << "error: The number of sample points is 0." << endl;
        exit(-1);
    }
    else if (indexs.size() < 1000)
    {
        return indexs;
    }
    else
    {
        int num = indexs.size() * CoarseFactor;
        double step = 1 / CoarseFactor;
        vector<int> result(num);
        for (int i = 0; i < num; i++)
        {
            result[i] = i * step;
        }
        return result;
    }

}

//平面逐层采样
vector<vector<int>> PickingSpots::sampleGrid(int start_r, int rows, int start_c, int cols, int layers, bool flag) const
{
    std::vector<vector<int>> sampledPoints;
    auto Percentages = CoarseFactor;

    //全部采样
    if (flag)
    {
        for (int i = start_r; i< start_r + rows; i++)
        {
            for (int j = start_c; j < start_c + cols; j++)
            {
                sampledPoints.push_back({i,j});
            }
        }
        return sampledPoints;
    }

    //保证最小采样点在10%以上
    double Inner_Per = Min_Rate;
cout << "Inner_Per: " << Inner_Per << endl;
    // 计算最大层数
    int maxLayer = layers > 0 ? layers:min(rows, cols) / 2;
    double decrement = (Percentages - Inner_Per) / maxLayer;

    // 逐层采样
    for (int layer = 0; layer < maxLayer; ++layer)
    {
        // 计算当前层的边界
        int startRow = layer;
        int endRow = rows - 1 - layer;
        int startCol = layer;
        int endCol = cols - 1 - layer;

        // 计算当前层的周长
        int perimeter = 2 * ((endRow - startRow) + (endCol - startCol));

        // 计算当前层的采样点数
        int pointsInLayer = static_cast<int>(perimeter * Percentages);

        // 确保采样点数不为零
        pointsInLayer = max(pointsInLayer, 1);

        // 均匀采样当前层
        for (int p = 0; p < pointsInLayer; ++p)
        {
            // 计算采样点的位置
            double ratio = static_cast<double>(p) / pointsInLayer;
            //平面起始位置不一定为0
            int i = start_r, j = start_c;
            if (ratio < 0.25)
            {
                // 上边界
                i += startRow;
                j += startCol + static_cast<int>((endCol - startCol) * (ratio / 0.25));
            }
            else if (ratio < 0.5)
            {
                // 右边界
                i += startRow + static_cast<int>((endRow - startRow) * ((ratio - 0.25) / 0.25));
                j += endCol;
            }
            else if (ratio < 0.75)
            {
                // 下边界
                i += endRow;
                j += endCol - static_cast<int>((endCol - startCol) * ((ratio - 0.5) / 0.25));
            }
            else
            {
                // 左边界
                i += endRow - static_cast<int>((endRow - startRow) * ((ratio - 0.75) / 0.25));
                j += startCol;
            }

            // 将采样点存储为 vector<int>
            sampledPoints.push_back({i, j});
        }
        //计算下一层采样百分比
        Percentages -= decrement;
    }

    // 计算当前层的边界
    int startRow = maxLayer;
    int endRow = rows - 1 - maxLayer;
    int startCol = maxLayer;
    int endCol = cols - 1 - maxLayer;

    unordered_set<std::string> sampledSet;
    // 计算剩余区域的面积
    int remainingArea = (endRow - startRow + 1) * (endCol - startCol + 1);

    // 计算剩余区域的采样点数
    int pointsInRemaining = static_cast<int>(remainingArea * Inner_Per);

    // 确保采样点数不为零
    pointsInRemaining = max(pointsInRemaining, 1);

    // 均匀采样剩余区域
    for (int p = 0; p < pointsInRemaining; ++p)
    {
        int i = startRow + rand() % (endRow - startRow + 1);
        int j = startCol + rand() % (endCol - startCol + 1);

        // 将采样点存储为 vector<int>，并去重
        std::string key = std::to_string(i) + "," + std::to_string(j);
        if (sampledSet.find(key) == sampledSet.end())
        {
            sampledPoints.push_back({i, j});
            sampledSet.insert(key);
        }
    }
    return sampledPoints;
}
